using Microsoft.Extensions.Options;
using Secs4Net;
using Secs4Net.Options;
using Secs4Net.Sml;
using System.ComponentModel;
using SECSDevice;
using SECSDevice.Handler;
using SECSDevice.Model;
using static Secs4Net.Item;

namespace SimulateSECSDevice
{
    public partial class MainForm : Form
    {
        SecsGem? _secsGem;
        HsmsConnection? _connector;
        readonly ISecsGemLogger _logger;
        readonly BindingList<PrimaryMessageWrapper> recvBuffer = new BindingList<PrimaryMessageWrapper>();
        CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        List<SFModel> listSFModel = new List<SFModel>();
        readonly Dictionary<string, string> keyValuePairs = new Dictionary<string, string>()
        {
            {"S1","tvES"},{"S2","tvECD"},{"S3","tvMS"},{"S4","tvMC"},
            {"S5","tvEH"},{"S6","tvDC"},{"S7","tvPPM"},{"S8","tvCPT"},
            {"S9","tvSE"},{"S10","tvTS"},{"S11","tvHFS"},{"S12","tvWM"},
            {"S13","tvDST"},{"S14","tvOS"},{"S15","tvRM"},{"S16","tvPM"},
            {"S17","tvECD"},{"S18","tvSCD"}
        };

        public MainForm()
        {
            InitializeComponent();
            _logger = new SecsLogger(this.rtbLog);
            InitSxFy();
        }

        private void InitSxFy()
        {
            listSFModel.Clear();
            listSFModel = ParseTextToSF.ParseToSF();

            string nodeKey = string.Empty;
            foreach (SFModel model in listSFModel)
            {
                nodeKey = keyValuePairs.GetValueOrDefault(model.Type);
                if (!string.IsNullOrWhiteSpace(nodeKey))
                {
                    tvSF.Nodes.Find(nodeKey, true).First().Nodes.Add(model.Name);
                }
            }
            tvSF.ExpandAll();
        }

        private void _connector_ConnectionChanged(object? sender, ConnectionState e)
        {
            base.Invoke((MethodInvoker)delegate
            {
                lblStatus.Text = _connector.State.ToString();
            });
        }

        private async void btnSend_Click(object sender, EventArgs e)
        {
            if (_secsGem is null || string.IsNullOrWhiteSpace(txtSendContent.Text) || _connector?.State != ConnectionState.Selected)
            {
                return;
            }

            try
            {
                var reply = await _secsGem.SendAsync(txtSendContent.Text.ToSecsMessage());
                txtReciveContent.Text += reply.ToSml();
            }
            catch (SecsException ex)
            {
                txtReciveContent.Text = ex.Message;
            }
        }

        private void SetTextForControl(Control control, string message)
        {
            this.Invoke((MethodInvoker)delegate
            {
                control.Text = message;
            });
        }

        private void btnTest_Click(object sender, EventArgs e)
        {

        }

        private void btnClearReceive_Click(object sender, EventArgs e)
        {
            txtReciveContent.Clear();
        }

        private async void btnEnable_Click(object sender, EventArgs e)
        {
            _secsGem?.Dispose();
            if (_connector is not null)
            {
                await _connector.DisposeAsync();
            }

            var options = Options.Create(new SecsGemOptions
            {
                IsActive = rbActive.Checked,
                IpAddress = txtIP.Text,
                Port = (int)nudPort.Value,
                SocketReceiveBufferSize = 65536,
                DeviceId = (ushort)nudDeviceID.Value
            });

            _connector = new HsmsConnection(options, _logger);
            _secsGem = new SecsGem(options, _connector, _logger);

            _connector.ConnectionChanged += _connector_ConnectionChanged;

            btnEnable.Enabled = false;
            _ = _connector.StartAsync(_cancellationTokenSource.Token);
            ResponseMessage();

            btnDisable.Enabled = true;
        }

        private void ResponseMessage()
        {
            _ = _connector.ExecuteTask.ContinueWith(async asynct =>
            {
                await foreach (var e in _secsGem.GetPrimaryMessageAsync(_cancellationTokenSource.Token))
                {
                    using var primaryMessage = e.PrimaryMessage;
                    try
                    {
                        using var secondaryMessage = new SecsMessage(primaryMessage.S, (byte)(primaryMessage.F + 1))
                        {
                            Name = "test",
                            SecsItem = primaryMessage.SecsItem,
                        };
                        await e.TryReplyAsync(secondaryMessage, _cancellationTokenSource.Token);
                    }
                    catch (Exception ex)
                    {
                    }
                }
            });
        }

        private async void btnDisable_Click(object sender, EventArgs e)
        {
            if (!_cancellationTokenSource.IsCancellationRequested)
            {
                _cancellationTokenSource.Cancel();
                _cancellationTokenSource.Dispose();
            }
            if (_connector is not null)
            {
                await _connector.DisposeAsync();
            }
            _secsGem?.Dispose();
            _cancellationTokenSource = new CancellationTokenSource();

            _secsGem = null;
            btnEnable.Enabled = true;
            btnDisable.Enabled = false;
            lblStatus.Text = "Disable";
        }

        private async void tsmSendContent_Click(object sender, EventArgs e)
        {
            if (_secsGem is null || string.IsNullOrWhiteSpace(txtSendContent.Text) || _connector?.State != ConnectionState.Selected)
            {
                return;
            }

            try
            {
                var reply = await _secsGem.SendAsync(txtSendContent.Text.ToSecsMessage());
                txtReciveContent.Text += reply.ToSml();
            }
            catch (SecsException ex)
            {

            }
        }

        private void tvSF_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            txtSendContent.Clear();
            string message = string.Empty;

            listSFModel.ForEach(sf =>
            {
                if (sf.Name == e.Node.Text)
                {
                    message = sf.GetCommand();
                    return;
                }
            });

            SetTextForControl(txtSendContent, message);
        }

        private void tvSF_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                cms.Show(MousePosition.X + 20, MousePosition.Y);
            }
        }
    }
}
